﻿using System;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
using UnityEngine;

namespace JH
{
    /// <summary>
    ///   曲线顶点信息类，用于存储顶点的相关数据
    /// </summary>
    [Serializable]
#if ODIN_INSPECTOR
    [ReadOnly]
#endif
    public struct LineVertexInfo
    {
        /// <summary>
        /// 边界处理器
        /// </summary>
        [field: SerializeField]
#if ODIN_INSPECTOR
        [field: LabelText("边界处理器")]
#endif
        public BoundaryHandler BoundaryHandler { get; set; }

        /// <summary>
        ///  原始点数据
        /// </summary>
        [field: SerializeField]
#if ODIN_INSPECTOR
        [field: LabelText("原始点数据")]
#endif
        public Vector2 OriginPoint { get; set; }

#if UNITY_EDITOR

        /// <summary>
        ///  模型空间位置
        /// </summary>
        [field: SerializeField]
#if ODIN_INSPECTOR
        [field: LabelText("模型空间位置")]
#endif
        private Vector2 osPosition;

        /// <summary>
        ///  UV
        /// </summary>
        [field: SerializeField]
#if ODIN_INSPECTOR
        [field: LabelText("UV")]
#endif
        private Vector2 uv;

#endif

        /// <summary>
        ///  模型空间位置
        /// </summary>
        public Vector2 OSPosition =>
#if UNITY_EDITOR
            osPosition =
#endif
                BoundaryHandler.GetOSPositionByUV(UV);

        /// <summary>
        ///  颜色
        /// </summary>
        [field: SerializeField]
#if ODIN_INSPECTOR
        [field: LabelText("颜色")]
#endif
        public Color Color { get; set; }

        /// <summary>
        /// UV
        /// </summary>
        public Vector2 UV =>
#if UNITY_EDITOR
            uv =
#endif
                BoundaryHandler.GetUVByOriginPoint(OriginPoint);

        /// <summary>
        ///  左侧顶点的方向向量
        /// </summary>
        public Vector2? LeftDir { get; set; }

        /// <summary>
        /// 右侧顶点的方向向量
        /// </summary>
        public Vector2? RightDir { get; set; }

        /// <summary>
        ///  右侧法线向量，定义为右侧方向向量的逆时针旋转90度
        /// </summary>
        private Vector2? RightNormal
        {
            get
            {
                if (RightDir == null) return null;
                var dir = RightDir.Value.normalized;
                // 逆时针旋转90°
                return new Vector2(-dir.y, dir.x);
            }
        }

        /// <summary>
        ///   计算模型空间下，向右侧法线偏移distance距离后的位置
        /// </summary>
        private Vector2? GetRightOffset(float distance)
        {
            if (RightNormal == null) return null;

            // 计算右侧点
            return OSPosition + RightNormal.Value.normalized * distance;
        }

        /// <summary>
        ///  左侧法线向量，定义为左侧方向向量的顺针旋转90度
        /// </summary>
        private Vector2? LeftNormal
        {
            get
            {
                if (LeftDir == null) return null;

                var dir = LeftDir.Value.normalized;
                // 顺时针旋转90°
                return new Vector2(dir.y, -dir.x);
            }
        }

        /// <summary>
        /// 计算模型空间下，向左侧法线偏移distance距离后的位置
        /// </summary>
        private Vector2? GetLeftOffset(float distance)
        {
            if (LeftNormal == null) return null;

            // 计算左侧点
            return OSPosition + LeftNormal.Value.normalized * distance;
        }

        /// <summary>
        ///  夹角，定义为左侧方向向量  和 右侧方向向量之间的夹角，一定是从左侧方向向量顺时针旋转到右侧方向向量的弧度
        ///  单位是弧度
        /// </summary>
        public float? Angle
        {
            get
            {
                if (LeftDir == null || RightDir == null)
                    return null;

                // 如果左侧方向向量和右侧方向向量和为零向量，则返回180度
                var sumVec = LeftDir.Value + RightDir.Value;
                if (sumVec == Vector2.zero) return Mathf.PI;

                var a = LeftDir.Value.x;
                var b = LeftDir.Value.y;
                var c = RightDir.Value.x;
                var d = RightDir.Value.y;

                var dot = a * c + b * d;
                var cross = a * d - b * c;
                float rawAngle = Mathf.Atan2(cross, dot);

                // 转换为弧度
                return rawAngle <= 0 ? -rawAngle : (2 * Mathf.PI - rawAngle);
            }
        }

        /// <summary>
        ///  夹角的角平分线向量，注意夹角的定义，这里一定是夹角的平分线，
        ///  就是说，从左侧方向向量开始，顺时针旋转到右侧方向向量时，一定会经过夹角平分线向量的方向
        /// </summary>
        private Vector2? AngleDir
        {
            get
            {
                if (LeftDir == null || RightDir == null || Angle == null) return null;
                var sumVec = (LeftDir.Value + RightDir.Value).normalized;
                if (Mathf.Approximately(Angle.Value, (float)Math.PI)) return LeftNormal;
                return Angle.Value < Mathf.PI ? sumVec : -sumVec;
            }
        }

        /// <summary>
        ///  计算模型空间下，顶点向角平分线方向偏移distance距离后的点
        /// </summary>
        private Vector2? GetAngleBisectorOffset(float distance)
        {
            // 如果夹角或夹角方向为空，则返回null
            if (!AngleDir.HasValue || !Angle.HasValue) return null;

            // 求斜边长
            var sin = Mathf.Sin(Angle.Value / 2);
            var l = distance / (sin == 0 ? 1 : sin);

            // 计算夹角平分线方向的偏移点
            var calc = OSPosition + AngleDir.Value * l;

            // 计算uv
            var calcUV = BoundaryHandler.GetUVByOsPosition(calc);

            // 限定UV到0~1之间，当sin极小值时，l可能会非常大，导致计算出的点超出边界范围
            calcUV = new Vector2(Mathf.Clamp(calcUV.x, 0, 1), Mathf.Clamp(calcUV.y, 0, 1));

            // 返回计算后的点
            return BoundaryHandler.GetOSPositionByUV(calcUV);
        }

        /// <summary>
        ///  获取当前顶点对应的 UIVertex
        /// </summary>
        private UIVertex GetVertex()
        {
            return new UIVertex()
            {
                position = OSPosition,
                color = Color,
                uv0 = UV
            };
        }

        /// <summary>
        ///  获取左侧法线偏移后的顶点
        /// </summary>
        private UIVertex GetLeftVertex(float lineWidth)
        {
            if (LeftNormal == null) throw new Exception("左侧法线未计算");
            var leftPos = GetLeftOffset(lineWidth)!.Value;
            return new UIVertex()
            {
                position = leftPos,
                color = Color,
                uv0 = BoundaryHandler.GetUVByOsPosition(leftPos)
            };
        }

        /// <summary>
        ///  获取右侧法线偏移后的顶点
        /// </summary>
        private UIVertex GetRightVertex(float lineWidth)
        {
            if (RightNormal == null) throw new Exception("右侧法线未计算");
            var rightPos = GetRightOffset(lineWidth)!.Value;
            return new UIVertex()
            {
                position = rightPos,
                color = Color,
                uv0 = BoundaryHandler.GetUVByOsPosition(rightPos)
            };
        }

        /// <summary>
        ///  获取角平分线的偏移点
        /// </summary>
        private UIVertex GetAngleBisectorVertex(float lineWidth)
        {
            if (AngleDir == null) throw new Exception("夹角平分线未计算");

            var bisectorPos = GetAngleBisectorOffset(lineWidth)!.Value;
            return new UIVertex()
            {
                position = bisectorPos,
                color = Color,
                uv0 = BoundaryHandler.GetUVByOsPosition(bisectorPos)
            };
        }

        /// <summary>
        ///  获取当前点以及法线偏移后的点
        /// </summary>
        public (UIVertex current, UIVertex normal) GetCurrentAndNormalOffset(float lineWidth, bool isLeft = false)
        {
            return (GetVertex(), isLeft ? GetLeftVertex(lineWidth) : GetRightVertex(lineWidth));
        }

        /// <summary>
        ///  获取角平分线偏移点和右侧法线偏移点
        /// </summary>
        public (UIVertex calc, UIVertex rightNormal) GetAngleBisectorAndRightOffset(float lineWidth)
        {
            return (GetAngleBisectorVertex(lineWidth), GetRightVertex(lineWidth));
        }

        /// <summary>
        ///  获取当前点和角平分线偏移点
        /// </summary>
        public (UIVertex current, UIVertex calc) GetCurrentAndAngleBisectorOffset(float lineWidth)
        {
            return (GetVertex(), GetAngleBisectorVertex(lineWidth));
        }
    }
}